package grex.genes;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2003</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */
import grex.BNFException;
import grex.ErrorManager;
import grex.Options;
import grex.Prediction;
import grex.PredictionContainer;

import java.text.NumberFormat;

public class FuzzyTarget extends Gene implements ITerminal {

    private static final int NROFCILDREN = 0;
    private double value = -1;
    private Double prob = new Double(0);
    private Double lowerProb = new Double(-1);
    private int count = 0;
    private double[] support=null;
    private int majority=-1,totalSupport=0;
    private double[] probs=null;

    public FuzzyTarget() throws BNFException {
        super(NROFCILDREN);

    }

    public synchronized void execute(Gene parent, PredictionContainer data, int mode) throws GeneException {
        this.parent = parent;
        if (mode == Gene.TRAIN_MODE || mode == Gene.OPTIMIZE_MODE) {
            prob = new Double(0);
            lowerProb = new Double(0);
            count = data.values().size();
            if(ops.getPROBLEM_TYPE() == Options.CLASSIFICATION){
                 support = new double[environment.getNrOfTargetCategories()];
            }else{
                support = new double[1];
            }
            majority=-1;
            probs=null;
            if(ops.getPROBLEM_TYPE() != Options.CLASSIFICATION){
                value = calcRegValue(data);
            }
        }
    }
    private double calcRegValue(PredictionContainer data){
        double value=0;
        for (Prediction p : data.values()) {
            value+=p.getTargetValue()/data.values().size();
        }
        return value;
    }

    private void initValue() {
        if (value == -1) {
            if(ops.getPROBLEM_TYPE() == Options.CLASSIFICATION){
                 support = new double[environment.getNrOfTargetCategories()];
            }else{
                support = new double[1];
            }
            if (parent instanceof If || parent instanceof FuzzySet) {
                Gene[] childs = parent.getChildren();
                Gene neighbour;
                if (childs[1] == this) {
                    neighbour = childs[2];
                } else {
                    neighbour = childs[1];
                }
                if (neighbour instanceof FuzzyTarget) {
                    int tries = 0;
                    value = -1;
                    while ((value == -1 || value == ((FuzzyTarget) neighbour).getValue()) && tries < 100) {
                        value = environment.getRandomTargetValue();
                        tries++;
                    }
                } else {
                    value = environment.getRandomTargetValue();
                }
            } else {
                value = environment.getRandomTargetValue();
            }
        }
    }

    public void setValue(double value) {
        this.value = value;
    }

    public double getValue() {
        initValue();
        return value;
    }

    public void setProb(double prob) {
        this.prob = prob;
    }

    public double getProb() {
        return getProbs()[majority];
    }

    public Object clone() {
        FuzzyTarget newFuzzyTarget;
        try {
            newFuzzyTarget = new FuzzyTarget();
            newFuzzyTarget.setOptions(ops);
            newFuzzyTarget.setEnvironment(environment);
            newFuzzyTarget.setValue(new Double(value));
            newFuzzyTarget.setProb(new Double(prob));
            newFuzzyTarget.lowerProb=new Double(lowerProb);
            newFuzzyTarget.setCount(count);
            newFuzzyTarget.setParent(parent);
            newFuzzyTarget.support=support;
        } catch (grex.BNFException e) {
            ErrorManager.getInstance().reportError(e);
            return null;
        }
        return newFuzzyTarget;
    }

    /** @todo Om endast enn kategori finns kommer denna att volja lika ofta somma alla andra sammtidigt */
    public String toString() {
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(2);
        init();
        if (super.ops.getPROBLEM_TYPE() == Options.CLASSIFICATION) {
            return environment.getRealTargetClassValue(value);
        } else {
            return "" + nf.format(value);
        }
    }
        private void init(){
           if(support!=null && majority ==-1){
            totalSupport = 0;
            majority = 0;
            double maxSupport =0;
            for(int i = 0;i<support.length;i++){
                    totalSupport += support[i];
                    if(support[i]>maxSupport){
                        maxSupport = support[i];
                        majority = i;
                    }
            }        
        }     
    }
    
    public double[] getProbs() {
        if (probs == null){
            init();
            probs = new double[support.length];
            for (int i = 0; i < support.length; i++) {
                probs[i] = (1.0 + support[i]) / (support.length + totalSupport);
            }
        }
        return probs;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public double getLowProb() {
        return lowerProb;
    }
}

